package Adventure;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

public class Level 
{
	private String nameOfLevel = "";
	private int difficulty = 0;
	private Tile[][] tiles;
	private BufferedReader reader;
	
	// The level's objects in multiple ArrayLists.
	private ArrayList<String> trapLore;
	private ArrayList<String> harmfulTrapLore;
	private ArrayList<String> weaponList;
	private ArrayList<String> solidObjects;
	private ArrayList<String> otherObjects;
	private ArrayList<String> enemies;
	
	public Level(int _difficulty, String _name)
	{
		nameOfLevel = _name;
		difficulty = _difficulty;
		
		// Initialize the levels ArrayList.
		trapLore = new ArrayList<String>();
		harmfulTrapLore = new ArrayList<String>();
		weaponList = new ArrayList<String>();
		solidObjects = new ArrayList<String>();
		otherObjects = new ArrayList<String>();
		enemies = new ArrayList<String>();
	}
	
	public String getNameOfLevel()
	{
		return nameOfLevel;
	}
	
	public void setNameOfLevel(String _name)
	{
		nameOfLevel = _name;
	}
	
	public int getDifficulty()
	{
		return difficulty;
	}
	
	public void setDificulty(int _difficulty)
	{
		difficulty = _difficulty;
	}
	
	/**
	 * Load's an level based of an file
	 * @throws FileNotFoundException
	 */
	public void loadLevel(int _difficulty, String _fileName, int _rowSize, int _colSize) throws FileNotFoundException
	{
		ArrayList<String> readStrings = new ArrayList<String>();
		tiles = new Tile[_rowSize][_colSize];
		reader = new BufferedReader(new FileReader(_fileName));
		difficulty = _difficulty;
	
		// Go through the file and add the lines of text to an ArrayList<String>
		try 
		{
			while(reader.ready())
			{
				String lineOfNumbers = reader.readLine();
				readStrings.add(lineOfNumbers);
			}
		} 
		catch (IOException e)
		{
			System.out.println("IOException found!");
		}
		finally
		{
			try
			{
				reader.close();
			} 
			catch (IOException e)
			{
				System.out.println("IOException when closing!");
			}
		}
		
		/*	After reading and adding all input from the text file
		 *	Go through the ArrayList<String> and split each string to add
		 *	the Tile's on correct position depending on number read.
		 */
		for(int i = 0; i < readStrings.size(); i++)
		{
			String[] splitAreas = readStrings.get(i).split(" ");
			
			for(int j = 0; j < _colSize; j++)
			{
				tiles[i][j] = new Tile(Integer.parseInt(splitAreas[j]));
			}
		}
	}
	
	public void printLevel()
	{		
		for(int i = 0; i < tiles.length; i++)
		{
			for(int j = 0; j < tiles[i].length; j++)
			{
				System.out.print(tiles[i][j].getTileNumber() + " ");

			}
			
			System.out.println();
		}
	}
	
	public Tile[][] retrieveGameTiles()
	{
		return tiles;
	}
	
	/*******************************************
	 *  Methods for world generation.
	 ******************************************/
	public void generatePaths()
	{
		for(int rows = 0; rows < tiles.length; rows++)
		{
			for(int columns = 0; columns < tiles[rows].length; columns++)
			{
				// If the Tile is in the top left corner
				if(rows == 0 && columns == 0)
				{
					tiles[rows][columns].addPathToTile(2);
					tiles[rows][columns].addPathToTile(3);
					tiles[rows][columns].addPathToTile(7);
				}// If the Tile is bottom right corner
				else if(rows == tiles.length-1 && columns == tiles[rows].length-1)
				{
					tiles[rows][columns].addPathToTile(0);
					tiles[rows][columns].addPathToTile(1);
					tiles[rows][columns].addPathToTile(4);
				}// If the Tile is in the top right corner
				else if(rows == tiles.length-1 && columns == 0)
				{
					tiles[rows][columns].addPathToTile(1);
					tiles[rows][columns].addPathToTile(2);
					tiles[rows][columns].addPathToTile(6);
				}// If the Tile is in the bottom left corner
				else if(rows == 0 && columns == tiles[rows].length -1)
				{
					tiles[rows][columns].addPathToTile(0);
					tiles[rows][columns].addPathToTile(3);
					tiles[rows][columns].addPathToTile(5);
				}// Check if it is the first row
				else if(rows == 0)
				{
					tiles[rows][columns].addPathToTile(0);
					tiles[rows][columns].addPathToTile(2);
					tiles[rows][columns].addPathToTile(3);
					tiles[rows][columns].addPathToTile(5);
					tiles[rows][columns].addPathToTile(7);
				}// Check if its in the top right row
				else if(rows == tiles.length-1)
				{
					tiles[rows][columns].addPathToTile(0);
					tiles[rows][columns].addPathToTile(1);
					tiles[rows][columns].addPathToTile(2);
					tiles[rows][columns].addPathToTile(4);
					tiles[rows][columns].addPathToTile(6);
				}// Check whether the column is top
				else if(columns == 0)
				{
					tiles[rows][columns].addPathToTile(1);
					tiles[rows][columns].addPathToTile(2);
					tiles[rows][columns].addPathToTile(3);
					tiles[rows][columns].addPathToTile(6);
					tiles[rows][columns].addPathToTile(7);
				}// Check whether the column is at the bottom
				else if(columns == tiles[rows].length-1)
				{
					tiles[rows][columns].addPathToTile(0);
					tiles[rows][columns].addPathToTile(1);
					tiles[rows][columns].addPathToTile(3);
					tiles[rows][columns].addPathToTile(4);
					tiles[rows][columns].addPathToTile(5);
				}// Rest of the tiles
				else
				{
					tiles[rows][columns].addPathToTile(0);
					tiles[rows][columns].addPathToTile(1);
					tiles[rows][columns].addPathToTile(2);
					tiles[rows][columns].addPathToTile(3);
					tiles[rows][columns].addPathToTile(4);
					tiles[rows][columns].addPathToTile(5);
					tiles[rows][columns].addPathToTile(6);
					tiles[rows][columns].addPathToTile(7);
				}
			}
		}
	}
	
	/**
	 * Generate the worlds game objects
	 */ 
	public void generateGameObjects()
	{
		// Call to generate the level objects
		generateTrapLore();
		generateWeapons();
		generateEnemies();
		generateSolidObjects();
		generateEquippableObjects();
		
		for(int i = 0; i < tiles.length; i++)
		{
			for(int j = 0; j < tiles[i].length; j++)
			{
				int tileNum = tiles[i][j].getTileNumber();
				
				// Generate game objects depending on RNG.
				spawnObject(tileNum, i, j);
			}
		}
		
	}
	/*****************************************************************************
	 * 		METHODS TO HANDLE FILLING AND GENERATING THE LEVELS GAME OBJECTS
	 *****************************************************************************/
	
	
	/**
	 * Method to generate the level's lore for the game's generated traps.
	 */
	private void generateTrapLore()
	{
		// Deadly traps:
		addTrapLore("You got hit by an arrow to the knee, you bleed out and die");
		addTrapLore("Music starts to play, and the loop di loop starts to loop.. you rush away to avoid the horrific sounds, hits the wall and die");
		addTrapLore("You hear a distant sound comming from the roof... huge spikes fall down and you die");
		addTrapLore("You hear some distant squealing sounds comming closer to your location.. you see hundreds of rats rushing towards you with the aim to devour you");
		addTrapLore("Lasers starts appearing on the sides of the room.. slowly going through the room and slicing you in to thousands of small pieces");
		addTrapLore("The ground disappeares and you fall to your death");
		addTrapLore("You stumble on your foot and break your neck");
		
		// Harmful traps:
		addHarmfulTrapLore("A dog rushes past you and scrapes your leg, you got hurt and lost some health");
		addHarmfulTrapLore("You cut yourself on the rocky walls in the room, you lost some health");
		addHarmfulTrapLore("Gravity in this room is shifted, you fly up to the roof and scrape your body from the rocky edges on the roof");
		addHarmfulTrapLore("The air is thin in this room... you get hurt by the lack of oxygen");
		addHarmfulTrapLore("Bright lights starts to shine from the roof, you cover your eyes but are hurt from the strong lights");

	}
	
	/**
	 * Simple helper method to add lore to a deadly trap.
	 * @param _lore The string to add as the lore
	 */
	private void addTrapLore(String _lore)
	{
		trapLore.add(_lore);
	}
	
	/**
	 * Simple helper method to add lore to a deadly trap.
	 * @param _lore The string to add as the lore
	 */
	private void addHarmfulTrapLore(String _lore)
	{
		harmfulTrapLore.add(_lore);
	}
	
	private void generateWeapons()
	{
		addLevelWeapon("Laser");
		addLevelWeapon("Gun");
		addLevelWeapon("AK47");
		addLevelWeapon("Bat");
		addLevelWeapon("Bazooka");
		addLevelWeapon("Grenade");
		addLevelWeapon("SNG");
		addLevelWeapon("Magnum");
		addLevelWeapon("Sniper Rifle");
	}
	
	private void addLevelWeapon(String _weaponName)
	{
		weaponList.add(_weaponName);
	}
	
	private void generateEnemies()
	{
		addLevelEnemy("Scout");
		addLevelEnemy("Knight");
		addLevelEnemy("Rogue");
		addLevelEnemy("Gnome");
		addLevelEnemy("King");
		addLevelEnemy("Goul");
		addLevelEnemy("Skeleton");
		addLevelEnemy("Polterguise");
		addLevelEnemy("CokeDrinker");
		addLevelEnemy("Crazy");
	}
	
	private void addLevelEnemy(String _enemyName)
	{
		enemies.add(_enemyName);
	}
	
	private void generateSolidObjects()
	{
		addSolidObject("Stone");
		addSolidObject("Boulder");
		addSolidObject("Mime");
		addSolidObject("Metal Sphere");
		addSolidObject("Sleeping man");
		addSolidObject("Glass Cone");
		addSolidObject("Wooden Cube");
	}
	
	private void addSolidObject(String _solidObjectName)
	{
		solidObjects.add(_solidObjectName);
	}
	
	private void generateEquippableObjects()
	{
		addEquippableObject("Flashlight");
		addEquippableObject("Snacks");
		addEquippableObject("Coke");
		addEquippableObject("Knife");
		addEquippableObject("Rope");
	}
	
	private void addEquippableObject(String _objectName)
	{
		otherObjects.add(_objectName);
	}
	
	/*****************************************************************************
	 * 		END OF METHODS TO HANDLE FILLING AND GENERATING THE LEVELS GAME OBJECTS
	 *****************************************************************************/
	
	
	/**
	 * Method to spawn game object depending on RNG and tile number
	 * @param _tileNumber Decides the RNG of each object on that Tile
	 */
	private void spawnObject(int _tileNumber, int _row, int _column)
	{
		// Random generator to use throughout the program
		Random random = new Random();
		
		switch(_tileNumber)
		{
			case 0:
			{
				if(random.nextInt(10000)+1 < 3) // Extremely low chance - most likely will never happen. - 0.03% chance
				{
					tiles[_row][_column].spawnDeadlyTrap(trapLore);
				}
				else if(random.nextInt(1000) + 1 < 30) // Low chance - 3% chance
				{
					tiles[_row][_column].spawnTrap(10, harmfulTrapLore);
				}
				
				if(random.nextInt(1000) + 1 < 100) // Normal chance - 10% chance
				{
					tiles[_row][_column].spawnSolidObject(solidObjects);
				}
				
				if(random.nextInt(100) + 1 < 34) // High chance - 33% chance
				{
					tiles[_row][_column].spawnWeapon(weaponList);
				}
				
				if(random.nextInt(100) + 1 < 64) // Very high chance - 66% chance
				{
					tiles[_row][_column].spawnProjectile(2, 12);
				}
				
				if(random.nextInt(100) + 1 < 20) // Enemy spawn chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				break;
			}
			case 1:
			{
				if(random.nextInt(10000)+1 < 3) // Extremely low chance - most likely will never happen. - 0.03% chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				if(random.nextInt(1000) + 1 < 30) // Low chance - 3% chance
				{
					tiles[_row][_column].spawnSolidObject(solidObjects);
				}
				
				if(random.nextInt(1000) + 1 < 100) // Normal chance - 10% chance
				{
					tiles[_row][_column].spawnTrap(10, harmfulTrapLore);
				}
				
				if(random.nextInt(100) + 1 < 34) // High chance - 33% chance
				{
					tiles[_row][_column].spawnWeapon(weaponList);
				}
				
				if(random.nextInt(100) + 1 < 64) // Very high chance - 66% chance
				{
					tiles[_row][_column].spawnEquippableObject(otherObjects);
				}
				
				if(random.nextInt(100) + 1 < 15) // Enemy spawn chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				break;
			}
			case 2:
			{
				if(random.nextInt(10000)+1 < 3) // Extremely low chance - most likely will never happen. - 0.03% chance
				{
					tiles[_row][_column].spawnEquippableObject(otherObjects);
				}
				
				if(random.nextInt(1000) + 1 < 30) // Low chance - 3% chance
				{
					tiles[_row][_column].spawnTrap(15, harmfulTrapLore);
				}
				else if(random.nextInt(1000) + 1 < 100) // Normal chance - 10% chance
				{
					tiles[_row][_column].spawnTrap(10, harmfulTrapLore);
				}
				
				if(random.nextInt(100) + 1 < 34) // High chance - 33% chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				if(random.nextInt(100) + 1 < 64) // Very high chance - 66% chance
				{
					tiles[_row][_column].spawnSolidObject(solidObjects);
				}
				
				if(random.nextInt(100) + 1 < 10) // Enemy spawn chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				break;
			}
			case 3:
			{
				if(random.nextInt(10000)+1 < 3) // Extremely low chance - most likely will never happen. - 0.03% chance
				{
					tiles[_row][_column].spawnTrap(5, harmfulTrapLore);
				}
				
				if(random.nextInt(1000) + 1 < 30) // Low chance - 3% chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				if(random.nextInt(1000) + 1 < 100) // Normal chance - 10% chance
				{
					tiles[_row][_column].spawnEquippableObject(otherObjects);
				}
				
				if(random.nextInt(100) + 1 < 34) // High chance - 33% chance
				{
					tiles[_row][_column].spawnProjectile(4, 6);
				}
				
				if(random.nextInt(100) + 1 < 64) // Very high chance - 66% chance
				{
					tiles[_row][_column].spawnWeapon(weaponList);
				}
				
				if(random.nextInt(100) + 1 < 40) // Enemy spawn chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				break;
			}
			case 4:
			{
				if(random.nextInt(10000)+1 < 3) // Extremely low chance - most likely will never happen. - 0.03% chance
				{
					tiles[_row][_column].spawnTrap(10, harmfulTrapLore);
				}
				
				if(random.nextInt(1000) + 1 < 30) // Low chance - 3% chance
				{
					tiles[_row][_column].spawnSolidObject(solidObjects);
				}
				
				if(random.nextInt(1000) + 1 < 100) // Normal chance - 10% chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				if(random.nextInt(100) + 1 < 34) // High chance - 33% chance
				{
					tiles[_row][_column].spawnEquippableObject(otherObjects);
				}
				
				if(random.nextInt(100) + 1 < 64) // Very high chance - 66% chance
				{
					tiles[_row][_column].spawnProjectile(12, 20);
				}				
				
				if(random.nextInt(100) + 1 < 25) // Enemy spawn chance
				{
					tiles[_row][_column].spawnEnemy(enemies);
				}
				
				break;
			}
			default:System.out.println("Wrong tileNumber!"); break;
		}
		
	}
	
	public void clearLevelOfObjects()
	{
		System.out.println("Starting clearing the level from it's objects");
		
		trapLore.clear();
		harmfulTrapLore.clear();
		weaponList.clear();
		solidObjects.clear();
		otherObjects.clear();
		enemies.clear();
		
		System.out.println("Finished clearing all objects in the level");
	}
	
	public int generateRandomNumber(int _minNumber, int _maxNumber)
	{
		Random random = new Random();
		return random.nextInt(_maxNumber)+_minNumber;
	}
	
	/******************************************************************
	 * 			MAP CREATION TOOLS - BASIC/ADVANCED LEVELS.
	 ******************************************************************/
	public void createMap(int _size, String _nameOfMap)
	{
		BufferedWriter writer = null;
		tiles = new Tile[_size][_size];
		try 
		{
			writer = new BufferedWriter(new FileWriter(new File(_nameOfMap)));
		} 
		catch (IOException e) 
		{
			System.out.println("IO Exception when creating the Writer.");
		}catch (NullPointerException e)
		{
			System.out.println("Writer failed to initialize, because of NullPointerException");
			e.printStackTrace();
		}
		
		for(int i = 0; i < _size; i++)
		{
			for(int j = 0; j < _size; j++)
			{
				try 
				{
					writeStringToFile(writer, "" + generateRandomNumber(0, 5) + " ");
				} 
				catch (IOException e)
				{
					System.out.println("IOException when writing to an file!");
					e.printStackTrace();
				}
			}
			
			try 
			{
				writer.newLine();
			}
			catch (IOException e) 
			{
				System.out.println("Failed to create a new Line.");
			}
		}
		
		System.out.println("Finished creating the new map at " + _nameOfMap);
		
		try 
		{
			writer.close();
		}
		catch (IOException e) 
		{
			System.out.println("Error closing the writer");
		}
				
	}
	
	// Simple helper method for CreateMap and CreateAdvancedMap methods.
	protected void writeStringToFile(BufferedWriter _writer, String _textString) throws IOException
	{
		_writer.write(_textString);
	}
	
	public void createAdvancedMap(int _rowSize, int _colSize, String _nameOfMap)
	{
		BufferedWriter writer = null;
		try 
		{
			writer = new BufferedWriter(new FileWriter(new File(_nameOfMap)));
		} 
		catch (IOException e) 
		{
			System.out.println("IO Exception when creating the Writer.");
		}catch (NullPointerException e)
		{
			System.out.println("Writer failed to initialize, because of NullPointerException");
			e.printStackTrace();
		}
		
		for(int i = 0; i < _rowSize; i++)
		{
			for(int j = 0; j < _colSize; j++)
			{
				try 
				{
					writeStringToFile(writer, "" + generateRandomNumber(0, 5) + " ");
				} 
				catch (IOException e)
				{
					System.out.println("IOException when writing to an file!");
					e.printStackTrace();
				}
			}
			
			try 
			{
				writer.newLine();
			}
			catch (IOException e) 
			{
				System.out.println("Failed to create a new Line.");
			}
		}
		
		System.out.println("Finished creating the new map at " + _nameOfMap);
		
		try 
		{
			writer.close();
		}
		catch (IOException e) 
		{
			System.out.println("Error closing the writer");
		}
		
	}
	
}
